import pandas as pd
import smtplib
import ssl
import socket
import time
from email.mime.text import MIMEText
from email.utils import formataddr
import re

from script_executor.Domain import Domain, InputItem


class EmailSend(Domain):

    def __init__(self):
        self.smtp_server = None
        self.smtp_port = None
        self.encryption = None
        self.sender_email = None
        self.sender_password = None
        self.sender_name = None
        self.mail_content = None


    def get_input_items(self) -> list[InputItem]:
        return [
            InputItem(
                label="smtp_server",
                input_name="smtp_server",
                input_type="text",
                default_value="smtp.qq.com",
                placeholder_text="请输入smtp服务器地址",
                options=[],
                required=True
            ),
            InputItem(
                label="smtp_port",
                input_name="smtp_port",
                input_type="text",
                default_value="465",
                placeholder_text="请输入端口",
                options=[],
                required=True
            ),
            InputItem(
                label="协议",
                input_name="encryption",
                input_type="select",
                default_value="ssl",
                placeholder_text="请输入端口（587为TLS，465为SSL）",
                options=['ssl','tls'],
                required=True
            ),
            InputItem(
                label="发件人邮箱",
                input_name="sender_email",
                input_type="text",
                default_value="",
                placeholder_text="请输入发件人邮箱",
                options=[],
                required=True
            ),
            InputItem(
                label="发件人邮箱密钥或授权码",
                input_name="sender_password",
                input_type="text",
                default_value="",
                placeholder_text="请输入发件人邮箱密钥或授权码",
                options=[],
                required=True
            ),
            InputItem(
                label="发件人名称",
                input_name="sender_name",
                input_type="text",
                default_value="",
                placeholder_text="请输入发件人名称，比如人力资源部",
                options=[],
                required=True
            ),
            InputItem(
                label="邮件内容EXCEL文件",
                input_name="mail_content",
                input_type="file",
                default_value="",
                placeholder_text="请输入邮件内容EXCEL文件，需要包含这样几列：收件人、邮件标题、邮件正文，邮件标题和邮件正文里可以引入一些变量，比如邮件正文这样写：你好，{姓名}，那么需要有一列叫做姓名",
                options=[]
            )
        ]

    def execute(self) -> str:
        sender = ExcelEmailSender(smtp_server=self.smtp_server, port=self.smtp_port, sender_email=self.sender_email,
                                  sender_password=self.sender_password, encryption=self.encryption, sender_name=self.sender_name)

        # 替换为你的Excel文件路径并发送邮件
        sender.process_and_send(self.mail_content)

        return '发送成功'


class ExcelEmailSender:
    def __init__(self, smtp_server, port, sender_email, sender_password, encryption, sender_name=None):
        """
        初始化邮件发送器
        :param smtp_server: SMTP服务器地址
        :param port: 端口号
        :param sender_email: 发件人邮箱
        :param sender_password: 发件人密码/授权码
        :param encryption: 加密方式，可选 'ssl' 或 'tls'
        :param sender_name: 发件人显示名称（可选）
        """
        self.smtp_server = smtp_server
        self.port = port
        self.sender_email = sender_email
        self.sender_password = sender_password
        self.encryption = encryption.lower()  # 转为小写，支持大小写不敏感
        self.sender_name = sender_name if sender_name else sender_email.split('@')[0]

        # 验证加密方式是否有效
        if self.encryption not in ['ssl', 'tls']:
            raise ValueError("加密方式必须是 'ssl' 或 'tls'")

        # 配置SSL上下文，提高兼容性
        self.ssl_context = ssl.create_default_context()
        self.ssl_context.set_ciphers('DEFAULT@SECLEVEL=1')
        self.ssl_context.options |= ssl.OP_NO_TLSv1_3  # 禁用TLSv1.3以兼容部分服务器

    def read_excel(self, file_path):
        """读取Excel文件内容"""
        try:
            df = pd.read_excel(file_path)
            required_columns = ['收件人', '邮件标题', '邮件正文']
            for col in required_columns:
                if col not in df.columns:
                    raise ValueError(f"Excel文件缺少必要的列: {col}")
            return df
        except Exception as e:
            print(f"读取Excel文件失败: {str(e)}")
            return None

    def replace_placeholders(self, text, params):
        """替换文本中的占位符 {参数名}"""
        pattern = r'\{(\w+)\}'
        placeholders = re.findall(pattern, text)

        for placeholder in placeholders:
            if placeholder in params:
                text = text.replace(f'{{{placeholder}}}', str(params[placeholder]))

        return text

    def send_email(self, recipient, subject, content):
        """发送单封邮件，忽略退出阶段的非关键错误"""
        try:
            # 创建邮件内容
            msg = MIMEText(content, 'html', 'utf-8')
            msg['From'] = formataddr((self.sender_name, self.sender_email))
            msg['To'] = recipient
            msg['Subject'] = subject

            # 测试服务器基础连接
            try:
                with socket.create_connection((self.smtp_server, self.port), timeout=10):
                    pass  # 连接成功无需处理
            except Exception as e:
                print(f"无法连接到服务器 {self.smtp_server}:{self.port}: {str(e)}")
                return False

            # 根据选择的加密方式创建连接
            if self.encryption == 'ssl':
                server = smtplib.SMTP_SSL(
                    self.smtp_server,
                    self.port,
                    context=self.ssl_context,
                    timeout=15
                )
            else:  # tls
                server = smtplib.SMTP(
                    self.smtp_server,
                    self.port,
                    timeout=15
                )
                server.starttls(context=self.ssl_context)

            # 登录并发送邮件
            server.login(self.sender_email, self.sender_password)
            server.sendmail(self.sender_email, recipient, msg.as_string())
            print(f"邮件已成功发送至: {recipient}")

            # 单独处理退出阶段，忽略非关键错误
            try:
                server.quit()
            except Exception as e:
                print(f"忽略退出阶段异常 (不影响邮件发送): {str(e)}")

            return True

        except smtplib.SMTPException as e:
            print(f"SMTP错误: {str(e)}")
        except ssl.SSLError as e:
            print(f"SSL加密错误: {str(e)}")
        except socket.timeout:
            print("连接超时，请检查网络")
        except Exception as e:
            print(f"发送失败: {str(e)}")
        return False

    def process_and_send(self, excel_file):
        """处理Excel并发送所有邮件"""
        df = self.read_excel(excel_file)
        if df is None:
            return

        # 获取参数列
        param_columns = [col for col in df.columns if col not in ['收件人', '邮件标题', '邮件正文']]

        # 遍历发送邮件
        for index, row in df.iterrows():
            try:
                recipient = row['收件人']
                subject = row['邮件标题']
                content = row['邮件正文']

                # 收集参数并替换
                params = {col: row[col] for col in param_columns if pd.notna(row[col])}
                content = self.replace_placeholders(content, params)
                subject = self.replace_placeholders(subject, params)

                # 发送邮件
                self.send_email(recipient, subject, content)

                # 延迟避免频率限制
                time.sleep(2)

            except Exception as e:
                print(f"处理第 {index + 1} 行数据失败: {str(e)}")


if __name__ == "__main__":
    # 配置SMTP参数（根据你的邮箱修改）
    smtp_server = "smtp.qq.com"  # 例如: smtp.qq.com, smtp.gmail.com
    smtp_port = 465  # 通常是587或465
    sender_email = "xxx@qq.com"
    sender_password = "xxx"  # 部分邮箱需要应用专用密码

    # 创建发送器实例
    sender = ExcelEmailSender(smtp_server=smtp_server, port=smtp_port, sender_email=sender_email, sender_password=sender_password, encryption='ssl', sender_name='IT部')

    # 替换为你的Excel文件路径并发送邮件
    sender.process_and_send(r'C:\Users\xxx\Downloads\mails.xlsx')
